// Magic Software, Inc.
// http://www.magic-software.com
// Copyright (c) 2000, All Rights Reserved
//
// Source code from Magic Software is supplied under the terms of a license
// agreement and may not be copied or disclosed except in accordance with the
// terms of that agreement.  The various license agreements may be found at
// the Magic Software web site.  This file is subject to the license
//
// RESTRICTED USE SOURCE CODE
// http://www.magic-software.com/License/restricted.pdf

#include "MgcNode.h"

MgcImplementRTTI(MgcNode,MgcSpatial);
MgcImplementStream(MgcNode);

//---------------------------------------------------------------------------
MgcNode::MgcNode (unsigned int uiQuantity, unsigned int uiGrowBy)
    :
    m_aspkChild(uiQuantity)
{
    m_uiGrowBy = uiGrowBy;
    m_uiUsed = 0;
}
//---------------------------------------------------------------------------
MgcNode::~MgcNode ()
{
    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcSpatialPtr spkChild = DetachChildAt(uiI);
        spkChild = 0;
    }
}
//---------------------------------------------------------------------------
unsigned int MgcNode::GetQuantity () const
{
    return m_aspkChild.GetQuantity();
}
//---------------------------------------------------------------------------
unsigned int MgcNode::GetUsed () const
{
    return m_uiUsed;
}
//---------------------------------------------------------------------------
unsigned int MgcNode::AttachChild (MgcSpatial* pkChild)
{
    // assert:  pkChild is not already in array of children

    pkChild->SetParent(this);
    m_uiUsed++;

    // attach child in first available slot (if any)
    unsigned int uiQuantity = m_aspkChild.GetQuantity();
    for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
    {
        if ( m_aspkChild[uiI] == 0 )
        {
            m_aspkChild[uiI] = pkChild;
            return uiI;
        }
    }

    // all slots used, increase array size
    m_aspkChild.SetQuantity(uiQuantity + m_uiGrowBy,true);
    m_aspkChild[uiQuantity] = pkChild;
    return uiQuantity;
}
//---------------------------------------------------------------------------
unsigned int MgcNode::DetachChild (MgcSpatial* pkChild)
{
    if ( pkChild )
    {
        // search to see if child exists
        for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
        {
            if ( m_aspkChild[uiI] == pkChild )
            {
                // child found, detach it
                pkChild->SetParent(0);
                m_aspkChild[uiI] = 0;
                m_uiUsed--;
                return uiI;
            }
        }
    }

    return ~0;
}
//---------------------------------------------------------------------------
MgcSpatialPtr MgcNode::DetachChildAt (unsigned int uiI)
{
    if ( uiI < m_aspkChild.GetQuantity() )
    {
        MgcSpatialPtr spkChild = m_aspkChild[uiI];
        if ( spkChild )
        {
            // child exists in slot, detach it
            spkChild->SetParent(0);
            m_aspkChild[uiI] = 0;
            m_uiUsed--;
        }
        return spkChild;
    }
    else
    {
        return 0;
    }
}
//---------------------------------------------------------------------------
MgcSpatialPtr MgcNode::SetChild (unsigned int uiI, MgcSpatial* pkChild)
{
    if ( uiI < m_aspkChild.GetQuantity() )
    {
        // detach child currently in slot
        MgcSpatial* pkPreviousChild = m_aspkChild[uiI];
        if ( pkPreviousChild )
        {
            pkPreviousChild->SetParent(0);
            m_uiUsed--;
        }

        // attach new child to slot
        if ( pkChild )
        {
            pkChild->SetParent(this);
            m_uiUsed++;
        }

        m_aspkChild[uiI] = pkChild;
        return pkPreviousChild;
    }
    else
    {
        // index out of range, increase array size and attach new child
        pkChild->SetParent(this);
        m_aspkChild.SetQuantity(uiI + m_uiGrowBy,true);
        m_aspkChild[uiI] = pkChild;
        m_uiUsed++;
        return 0;
    }
}
//---------------------------------------------------------------------------
MgcSpatialPtr MgcNode::GetChild (unsigned int uiI)
{
    if ( uiI < m_aspkChild.GetQuantity() )
        return m_aspkChild[uiI];
    else
        return 0;
}
//---------------------------------------------------------------------------
void MgcNode::UpdateWorldData (MgcReal fAppTime)
{
    MgcSpatial::UpdateWorldData(fAppTime);

    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcSpatial* pkChild = m_aspkChild[uiI];
        if ( pkChild )
            pkChild->UpdateGS(fAppTime,false);
    }
}
//---------------------------------------------------------------------------
void MgcNode::UpdateWorldBound ()
{
    bool bFoundFirstBound = false;
    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcSpatial* pkChild = m_aspkChild[uiI];
        if ( pkChild )
        {
            if ( bFoundFirstBound )
            {
                // merge current world bound with child world bound
                m_kWorldBound += pkChild->WorldBound();
            }
            else
            {
                // set world bound to first non-null child world bound
                bFoundFirstBound = true;
                m_kWorldBound = pkChild->WorldBound();
            }
        }
    }
}
//---------------------------------------------------------------------------
void MgcNode::UpdateRenderState (MgcRenderState* apkState[])
{
    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcSpatial* pkChild = m_aspkChild[uiI];
        if ( pkChild )
            pkChild->UpdateRS(apkState);
    }
}
//---------------------------------------------------------------------------
void MgcNode::Draw (MgcRenderer& rkRenderer)
{
    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcSpatial* pkChild = m_aspkChild[uiI];
        if ( pkChild )
            pkChild->OnDraw(rkRenderer);
    }
}
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// streaming
//---------------------------------------------------------------------------
MgcObject* MgcNode::Factory (MgcStream& rkStream)
{
    MgcNode* pkObject = new MgcNode;
    MgcStream::Link* pkLink = new MgcStream::Link(pkObject);
    pkObject->Load(rkStream,pkLink);
    return pkObject;
}
//---------------------------------------------------------------------------
void MgcNode::Load (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcSpatial::Load(rkStream,pkLink);

    // native data
    MgcStreamRead(rkStream,m_uiGrowBy);
    MgcStreamRead(rkStream,m_uiUsed);

    // link data
    unsigned int uiQuantity;
    MgcStreamRead(rkStream,uiQuantity);
    for (unsigned int uiI = 0; uiI < uiQuantity; uiI++)
    {
        MgcSpatial* pkChild;
        MgcStreamRead(rkStream,pkChild);
        pkLink->Add(pkChild);
    }

    // set array quantity, array filled in by Link
    m_aspkChild.SetQuantity(uiQuantity,false);
}
//---------------------------------------------------------------------------
void MgcNode::Link (MgcStream& rkStream, MgcStream::Link* pkLink)
{
    MgcSpatial::Link(rkStream,pkLink);

    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        MgcObject* pkLinkID = pkLink->GetLinkID();
        MgcSpatial* pkChild = (MgcSpatial*)rkStream.GetFromMap(pkLinkID);
        SetChild(uiI,pkChild);
    }
}
//---------------------------------------------------------------------------
bool MgcNode::Register (MgcStream& rkStream)
{
    if ( !MgcSpatial::Register(rkStream) )
        return false;

    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
    {
        if ( m_aspkChild[uiI] )
            m_aspkChild[uiI]->Register(rkStream);
    }

    return true;
}
//---------------------------------------------------------------------------
void MgcNode::Save (MgcStream& rkStream)
{
    MgcSpatial::Save(rkStream);

    // native data
    MgcStreamWrite(rkStream,m_uiGrowBy);
    MgcStreamWrite(rkStream,m_uiUsed);

    // link data
    MgcStreamWrite(rkStream,m_aspkChild.GetQuantity());
    for (unsigned int uiI = 0; uiI < m_aspkChild.GetQuantity(); uiI++)
        MgcStreamWrite(rkStream,m_aspkChild[uiI]);
}
//---------------------------------------------------------------------------
